// Code copied from [Rome](https://github.com/rome/tools/blob/lsp/v0.28.0/npm/rome/scripts/generate-packages.mjs)

import * as fs from 'node:fs';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const OXLINT_BIN_NAME = 'oxlint';
const OXLS_BIN_NAME = 'oxc_language_server';
/** <REPO ROOT>/npm/oxlint` */
const OXLINT_ROOT = resolve(fileURLToPath(import.meta.url), '../..');
/** `<REPO ROOT>/npm` */
const PACKAGES_ROOT = resolve(OXLINT_ROOT, '..');
const REPO_ROOT = resolve(PACKAGES_ROOT, '..');
/** `<REPO ROOT>/npm/oxlint/package.json` */
const MANIFEST_PATH = resolve(OXLINT_ROOT, 'package.json');
/** `<REPO ROOT>/apps/oxlint/dist` */
const OXLINT_DIST_SRC = resolve(REPO_ROOT, 'apps/oxlint/dist');
/** `<REPO ROOT>/npm/oxlint/dist` */
const OXLINT_DIST_DEST = resolve(OXLINT_ROOT, 'dist');

/** Parsed `<REPO ROOT>/npm/oxlint/package.json` */
const rootManifest = JSON.parse(fs.readFileSync(MANIFEST_PATH).toString('utf-8'));

const LIBC_MAPPING = {
  gnu: 'glibc',
  musl: 'musl',
};

function generateNativePackage(target) {
  const packageName = `@${OXLINT_BIN_NAME}/${target}`;
  const packageRoot = resolve(PACKAGES_ROOT, `${OXLINT_BIN_NAME}-${target}`);

  // Remove the directory just in case it already exists (it's autogenerated
  // so there shouldn't be anything important there anyway)
  fs.rmSync(packageRoot, { recursive: true, force: true });

  // Create the package directory
  console.log(`Create directory ${packageRoot}`);
  fs.mkdirSync(packageRoot);

  // Generate the package.json manifest
  const { version, author, license, homepage, bugs, repository } = rootManifest;

  const triple = target.split('-');
  const platform = triple[0];
  const arch = triple[1];
  const libc = triple[2] && { libc: [LIBC_MAPPING[triple[2]]] };
  const manifest = {
    name: packageName,
    version,
    type: 'commonjs',
    main: `${OXLINT_BIN_NAME}.${target}.node`,
    author,
    license,
    homepage,
    bugs,
    repository,
    os: [platform],
    cpu: [arch],
    ...libc,
    publishConfig: {
      executableFiles: ['oxc_language_server'],
    },
  };

  const manifestPath = resolve(packageRoot, 'package.json');
  console.log(`Create manifest ${manifestPath}`);
  fs.writeFileSync(manifestPath, JSON.stringify(manifest));

  // Copy the binaries
  const oxlintBinSource = resolve(REPO_ROOT, `${OXLINT_BIN_NAME}.${target}.node`);
  const oxlintBinTarget = resolve(packageRoot, `${OXLINT_BIN_NAME}.${target}.node`);

  const ext = platform === 'win32' ? '.exe' : '';
  const oxlsBinSource = resolve(REPO_ROOT, `${OXLS_BIN_NAME}-${target}${ext}`);
  const oxlsBinTarget = resolve(packageRoot, `${OXLS_BIN_NAME}${ext}`);

  console.log(`Copy linter binary ${oxlintBinSource}`);
  fs.copyFileSync(oxlintBinSource, oxlintBinTarget);

  console.log(`Copy language server binary ${oxlsBinSource}`);
  fs.copyFileSync(oxlsBinSource, oxlsBinTarget);
  fs.chmodSync(oxlsBinTarget, 0o755);
}

function writeManifest() {
  /** `<REPO ROOT>/npm/oxlint/package.json` */
  const manifestPath = resolve(PACKAGES_ROOT, OXLINT_BIN_NAME, 'package.json');

  const manifestData = JSON.parse(fs.readFileSync(manifestPath).toString('utf-8'));

  const nativePackages = TARGETS.map((target) => [`@${OXLINT_BIN_NAME}/${target}`, rootManifest.version]);

  manifestData.version = rootManifest.version;
  manifestData.optionalDependencies = Object.fromEntries(nativePackages);

  // Do not automatically install 'oxlint-tsgolint'.
  // https://docs.npmjs.com/cli/v11/configuring-npm/package-json#peerdependenciesmeta
  manifestData.peerDependencies = {
    'oxlint-tsgolint': '>=0.7.1',
  };
  manifestData.peerDependenciesMeta = {
    'oxlint-tsgolint': {
      optional: true,
    },
  };

  console.log(`Update manifest ${manifestPath}`);
  const content = JSON.stringify(manifestData);
  fs.writeFileSync(manifestPath, content);
}

// Copy `dist` directory from `apps/oxlint/dist` to `npm/oxlint/dist`.
// `apps/oxlint/scripts/build.js` must be run before this script to create the `dist` directory.
function copyDistFiles() {
  fs.cpSync(OXLINT_DIST_SRC, OXLINT_DIST_DEST, { recursive: true });
}

// NOTE: Must update npm/oxlint/bin/oxc_language_server
const TARGETS = [
  'win32-x64',
  'win32-arm64',
  'linux-x64-gnu',
  'linux-arm64-gnu',
  'linux-x64-musl',
  'linux-arm64-musl',
  'darwin-x64',
  'darwin-arm64',
];

for (const target of TARGETS) {
  generateNativePackage(target);
}

writeManifest();
copyDistFiles();
